<?php

declare(strict_types=1);

namespace app\controller;

use app\BaseController;
use app\service\factory\ResponseFactory;
use app\service\misc\GeoRegionService;
use app\service\region\RegionService;
use app\service\user\AuthService;
use app\model\User;
use app\service\user\RegisterService;
use app\service\user\SmsCodeService;
use app\service\user\PartialTokenService;
use app\service\user\CompanyVerificationService;
use app\service\sys\ConfigService;
use app\util\QiniuUtil;
use InvalidArgumentException;
use think\Response;

class Auth extends BaseController
{
    public function login(AuthService $authService): Response
    {
        try {
            $type = $this->request->param('type');
            $credentials = $this->request->param('credentials');
            if (empty($type)) {
                return json(ResponseFactory::error('登录类型不能为空', 400));
            }

            if (empty($credentials) || !is_array($credentials)) {
                return json(ResponseFactory::error('登录凭证格式不正确', 400));
            }
            $result = $authService->login($type, $credentials);
            // 使用工厂创建统一响应
            $response = ResponseFactory::createApiResponse($result);

            return json($response);
        } catch (\Exception $e) {
            return json(ResponseFactory::error($e->getMessage(), $e->getCode() ?: 500));
        }
    }

    public function register(RegisterService $registerService): Response
    {
        try {
            $payload = $this->request->only([
                'phone',
                'password',
                'nickname',
                'real_name',
                'code',
                'identities',
                'identity_type',
                'register_channel',
                'region_id',
                'org_id',
                'invited_by',
                'invite_code',
                'partial_token',
                'partialToken',
            ], 'post');

            $result = $registerService->register($payload);
            $response = ResponseFactory::createApiResponse($result);

            return json($response);
        } catch (InvalidArgumentException $e) {
            return json(ResponseFactory::error($e->getMessage(), 422));
        } catch (\Throwable $e) {
            return json(ResponseFactory::error($e->getMessage(), $e->getCode() ?: 500));
        }
    }

    public function sendSmsCode(SmsCodeService $smsCodeService, PartialTokenService $partialTokenService): Response
    {
        try {
            $phone = trim((string) $this->request->param('phone'));
            $sceneRaw = (string) ($this->request->param('scene') ?? SmsCodeService::SCENE_BIND);
            $scene = match (trim(strtolower($sceneRaw))) {
                'bind', 'bind_phone', 'bindphone' => SmsCodeService::SCENE_BIND,
                'register', 'signup', 'reg' => SmsCodeService::SCENE_REGISTER,
                default => SmsCodeService::SCENE_LOGIN,
            };

            if ($scene === SmsCodeService::SCENE_BIND) {
                $this->validatePartialToken($partialTokenService);
            }

            if ($phone === '') {
                return json(ResponseFactory::error('手机号不能为空', 422));
            }

            $exists = User::where('phone', $phone)->count() > 0;
            if ($scene === SmsCodeService::SCENE_REGISTER && $exists) {
                return json(ResponseFactory::error('手机号已注册', 422));
            }
            if ($scene === SmsCodeService::SCENE_LOGIN && !$exists) {
                return json(ResponseFactory::error('账号不存在，请先注册', 422));
            }
            if ($scene === SmsCodeService::SCENE_BIND && $exists) {
                return json(ResponseFactory::error('该手机号已被其他账号绑定', 422));
            }

            $code = $smsCodeService->sendCode($scene, $phone);
            $data = [
                'sent' => true,
                'expires_in' => 300,
            ];
            if (config('app.app_debug')) {
                $data['debug_code'] = $code;
            }

            return json(ResponseFactory::success($data, '验证码已发送'));
        } catch (InvalidArgumentException $e) {
            return json(ResponseFactory::error($e->getMessage(), 422));
        } catch (\Throwable $e) {
            return json(ResponseFactory::error($e->getMessage(), 500));
        }
    }

    public function refreshToken(AuthService $authService): Response
    {
        try {
            $token = $this->request->param('refreshToken');
            if (empty($token)) {
                return json(ResponseFactory::error('刷新令牌不能为空', 400));
            }
            $result = $authService->refreshToken($token);
            // 使用工厂创建统一响应
            $response = ResponseFactory::createApiResponse($result);

            return json($response);
        } catch (\Exception $e) {
            return json(ResponseFactory::error($e->getMessage(), $e->getCode() ?: 500));
        }
    }

    public function getPartialStatus(AuthService $authService, PartialTokenService $partialTokenService): Response
    {
        try {
            $payload = $this->validatePartialToken($partialTokenService);

            $user = null;
            if (isset($payload['user_id'])) {
                $user = $authService->findUserById((int) $payload['user_id']);
            }

            $data = [
                'context' => $payload,
                'identities' => $user ? $authService->getUserIdentitiesData($user) : [],
                'company' => $authService->getUserCompanyData($user),
            ];

            return json(ResponseFactory::success($data));
        } catch (InvalidArgumentException $e) {
            return json(ResponseFactory::error($e->getMessage(), 422));
        } catch (\Throwable $e) {
            return json(ResponseFactory::error($e->getMessage(), $e->getCode() ?: 500));
        }
    }

    public function getCompanyVerification(
        AuthService $authService,
        PartialTokenService $partialTokenService,
        CompanyVerificationService $companyVerificationService
    ): Response {
        try {
            $user = $this->resolvePartialUser($authService, $partialTokenService);
            $company = $companyVerificationService->getCompany((int) $user->id);

            return json(ResponseFactory::success([
                'company' => $companyVerificationService->format($company),
                'identities' => $authService->getUserIdentitiesData($user),
            ]));
        } catch (InvalidArgumentException $e) {
            return json(ResponseFactory::error($e->getMessage(), 422));
        } catch (\Throwable $e) {
            return json(ResponseFactory::error($e->getMessage(), $e->getCode() ?: 500));
        }
    }

    public function saveCompanyVerification(
        AuthService $authService,
        PartialTokenService $partialTokenService,
        CompanyVerificationService $companyVerificationService
    ): Response {
        try {
            $user = $this->resolvePartialUser($authService, $partialTokenService);
            $payload = $this->request->only([
                'company_name',
                'license_code',
                'license_photo',
                'legal_rep',
                'address',
                'scope',
                'region_id',
                'raw',
            ], 'post');

            $company = $companyVerificationService->saveCompany((int) $user->id, $payload);
            $freshUser = $authService->findUserById((int) $user->id);

            return json(ResponseFactory::success([
                'company' => $companyVerificationService->format($company),
                'identities' => $authService->getUserIdentitiesData($freshUser),
            ], '认证资料已提交，待审核'));
        } catch (InvalidArgumentException $e) {
            return json(ResponseFactory::error($e->getMessage(), 422));
        } catch (\Throwable $e) {
            return json(ResponseFactory::error($e->getMessage(), $e->getCode() ?: 500));
        }
    }

    public function saveCompanyInvoice(
        AuthService $authService,
        PartialTokenService $partialTokenService,
        CompanyVerificationService $companyVerificationService
    ): Response {
        try {
            $user = $this->resolvePartialUser($authService, $partialTokenService);
            $payload = $this->request->only([
                'invoice_title',
                'invoice_taxno',
                'bank_name',
                'bank_account',
                'bank_account_name',
                'contact_name',
                'contact_phone',
                'contact_email',
            ], 'post');

            $company = $companyVerificationService->saveInvoice((int) $user->id, $payload);
            $freshUser = $authService->findUserById((int) $user->id);

            return json(ResponseFactory::success([
                'company' => $companyVerificationService->format($company),
                'identities' => $authService->getUserIdentitiesData($freshUser),
            ], '开票信息已保存'));
        } catch (InvalidArgumentException $e) {
            return json(ResponseFactory::error($e->getMessage(), 422));
        } catch (\Throwable $e) {
            return json(ResponseFactory::error($e->getMessage(), $e->getCode() ?: 500));
        }
    }

    public function saveCompanyAddresses(
        AuthService $authService,
        PartialTokenService $partialTokenService,
        CompanyVerificationService $companyVerificationService
    ): Response {
        try {
            $user = $this->resolvePartialUser($authService, $partialTokenService);
            $payload = $this->request->only([
                'addresses',
            ], 'post');

            $company = $companyVerificationService->saveAddresses((int) $user->id, $payload);
            $freshUser = $authService->findUserById((int) $user->id);

            return json(ResponseFactory::success([
                'company' => $companyVerificationService->format($company),
                'identities' => $authService->getUserIdentitiesData($freshUser),
            ], '地址信息已保存'));
        } catch (InvalidArgumentException $e) {
            return json(ResponseFactory::error($e->getMessage(), 422));
        } catch (\Throwable $e) {
            return json(ResponseFactory::error($e->getMessage(), $e->getCode() ?: 500));
        }
    }

    public function recognizeCompanyLicense(
        CompanyVerificationService $companyVerificationService
    ): Response {
        try {
            $imageUrl = (string) $this->request->post('image_url', '');
            $result = $companyVerificationService->recognizeBusinessLicense($imageUrl);

            return json(ResponseFactory::success($result, '营业执照识别成功'));
        } catch (InvalidArgumentException $e) {
            return json(ResponseFactory::error($e->getMessage(), 422));
        } catch (\Throwable $e) {
            return json(ResponseFactory::error($e->getMessage(), $e->getCode() ?: 500));
        }
    }

    public function menus(AuthService $authService): Response
    {
        try {
            $userId = $this->request->user_id ?? ($this->request->payload['user_id'] ?? null);
            if (!$userId) {
                return json(ResponseFactory::error('用户未登录', 401));
            }

            $user = $authService->findUserById((int)$userId);
            if (!$user) {
                return json(ResponseFactory::error('用户不存在', 404));
            }

            $data = [
                'profile' => $authService->getUserProfileData($user),
                'roles' => $authService->getUserRoles($user),
                'permissions' => $authService->getUserPermissionsCode($user),
                'menus' => $authService->getUserMenus($user),
                'identities' => $authService->getUserIdentitiesData($user),
            ];

            return json(ResponseFactory::success($data));
        } catch (\Throwable $e) {
            return json(ResponseFactory::error($e->getMessage(), $e->getCode() ?: 500));
        }
    }

    public function qiniu(ConfigService $configService): Response
    {
        try {
            $data = QiniuUtil::getUploadToken($configService);
            return json(ResponseFactory::success($data));
        } catch (\Throwable $e) {
            return json(ResponseFactory::error($e->getMessage(), $e->getCode() ?: 500));
        }
    }

    public function qiniuPartial(
        PartialTokenService $partialTokenService,
        ConfigService $configService
    ): Response {
        try {
            $this->validatePartialToken($partialTokenService);
            $data = QiniuUtil::getUploadToken($configService);
            return json(ResponseFactory::success($data));
        } catch (InvalidArgumentException $e) {
            return json(ResponseFactory::error($e->getMessage(), 422));
        } catch (\Throwable $e) {
            return json(ResponseFactory::error($e->getMessage(), $e->getCode() ?: 500));
        }
    }

    public function getVerificationRegions(
        PartialTokenService $partialTokenService,
        RegionService $regionService
    ): Response {
        try {
            $this->validatePartialToken($partialTokenService);
            $regions = $regionService->all(['status' => 1]);
            $formatted = array_map(static fn($item) => $regionService->format($item), $regions->all());
            $tree = $regionService->buildTree($formatted);
            return json(ResponseFactory::success($tree));
        } catch (InvalidArgumentException $e) {
            return json(ResponseFactory::error($e->getMessage(), 422));
        } catch (\Throwable $e) {
            return json(ResponseFactory::error($e->getMessage(), $e->getCode() ?: 500));
        }
    }

    public function getGeoRegions(
        PartialTokenService $partialTokenService,
        GeoRegionService $geoRegionService
    ): Response {
        try {
            $this->validatePartialToken($partialTokenService);
            $tree = $geoRegionService->tree();
            return json(ResponseFactory::success($tree));
        } catch (InvalidArgumentException $e) {
            return json(ResponseFactory::error($e->getMessage(), 422));
        } catch (\Throwable $e) {
            return json(ResponseFactory::error($e->getMessage(), $e->getCode() ?: 500));
        }
    }

    public function logout(AuthService $authService): Response
    {
        try {
            $isAll = $this->request->param('isAll', false);
            $payload = $this->request->payload ?? [];
            $userId = $this->request->user_id ?? ($payload['user_id'] ?? null);
            if (!$userId) {
                return json(ResponseFactory::error('用户未登录', 401));
            }
            $result = $authService->logout($userId, $isAll);
            $response = ResponseFactory::createApiResponse($result);
            return json($response);
        } catch (\Exception $e) {
            return json(ResponseFactory::error($e->getMessage(), $e->getCode() ?: 500));
        }
    }

    private function validatePartialToken(PartialTokenService $partialTokenService): array
    {
        $token = $this->extractBearerToken();
        if ($token === '') {
            throw new InvalidArgumentException('部分令牌不能为空');
        }

        try {
            return $partialTokenService->validatePartialToken($token);
        } catch (\Throwable $e) {
            throw new InvalidArgumentException($e->getMessage() ?: '部分令牌无效', $e->getCode() ?: 401);
        }
    }

    private function resolvePartialUser(AuthService $authService, PartialTokenService $partialTokenService): User
    {
        $payload = $this->validatePartialToken($partialTokenService);
        $userId = (int) ($payload['user_id'] ?? 0);
        if ($userId <= 0) {
            throw new InvalidArgumentException('部分令牌缺少用户信息');
        }

        $user = $authService->findUserById($userId);
        if (!$user) {
            throw new InvalidArgumentException('用户不存在或已删除');
        }

        return $user;
    }

    private function extractBearerToken(): string
    {
        $authorization = (string) $this->request->header('Authorization', '');
        if ($authorization === '') {
            return '';
        }

        if (preg_match('/^Bearer\s+(.*)$/i', $authorization, $matches)) {
            return trim($matches[1]);
        }

        return trim($authorization);
    }
}
